package com.service.totalauth.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName: RabbitConfig
 * @Description:
 * @Author guochuang
 * @Date 2021/10/25
 * @Version 1.0
 */

/**
 Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线，保证数据能按照指定的方式进行传输,
 Exchange：消息交换机,它指定消息按什么规则,路由到哪个队列。
 Queue:消息的载体,每个消息都会被投到一个或多个队列。
 Binding:绑定，它的作用就是把exchange和queue按照路由规则绑定起来.
 Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
 vhost:虚拟主机,一个broker里可以有多个vhost，用作不同用户的权限分离。
 Producer:消息生产者,就是投递消息的程序.
 Consumer:消息消费者,就是接受消息的程序.
 Channel:消息通道,在客户端的每个连接里,可建立多个channel.
 */
@Configuration
public class RabbitConfig implements RabbitListenerConfigurer {
//交换
public static final String EXCHANGE_A = "my-mq-exchange_A";
public static final String EXCHANGE_B = "my-mq-exchange_B";
public static final String EXCHANGE_C = "my-mq-exchange_C";

//队列
public static final String QUEUE_A = "QUEUE_A";
public static final String QUEUE_B = "QUEUE_B";
public static final String QUEUE_C = "QUEUE_C";
public static final String QUEUE_D = "QUEUE_D";
//路由key
public static final String ROUTING_KEY_A = "test.spring-boot-routingKey_A";
public static final String ROUTING_KEY_B = "test.spring-boot-routingKey_B";
public static final String ROUTING_KEY_C = "test.spring-boot-routingKey_C";
    /**
     * 正常队列
     */
    public static final String EXCHANGE = "boot-exchange";

    public static final String QUEUE = "boot-queue";

    public static final String ROUTING_KEY = "boot-rout";

    /**
     * 死信队列
     */
    public static final String DEAD_EXCHANGE = "dead-exchange";

    public static final String DEAD_QUEUE = "dead-queue";

    public static final String DEAD_ROUTING_KEY = "dead-rout";

@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
    final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
    return rabbitTemplate;
}

@Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
    return new Jackson2JsonMessageConverter();
}

@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar rabbitListenerEndpointRegistrar) {
    rabbitListenerEndpointRegistrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
}
@Bean
MessageHandlerMethodFactory messageHandlerMethodFactory() {
    DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
    messageHandlerMethodFactory.setMessageConverter(consumerJackson2MessageConverter());
    return messageHandlerMethodFactory;
}

@Bean
public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
    return new MappingJackson2MessageConverter();
}
/**
 * 针对消费者配置
 * 1. 设置交换机类型
 * 2. 将队列绑定到交换机
 FanoutExchange: 将消息分发到所有的绑定队列，无routingkey的概念
 HeadersExchange ：通过添加属性key-value匹配
 DirectExchange:按照routingkey分发到指定队列
 TopicExchange:多关键字匹配
 */
@Bean
public TopicExchange topicExchange() {
    return new TopicExchange(EXCHANGE_A,true,false);
}
/**
 * 获取队列A
 * @return
 */
@Bean
public Queue queueA() {
    return new Queue(QUEUE_A, true); //队列持久
}
    /**
     * 获取队列B
     * @return
     */
@Bean
public Queue queueB() {
    return new Queue(QUEUE_B, true); //队列持久
}
@Bean
public Queue queueC() {
    return new Queue(QUEUE_C, true); //队列持久
}
/**
 *  交换机可以绑定多个消息队列，可以分发到不同的队列当中去
 */

@Bean
public Binding binding() {
    return BindingBuilder.bind(queueA()).to(topicExchange()).with(ROUTING_KEY_A);
}
@Bean
public Binding bindingB() {
    return BindingBuilder.bind(queueB()).to(topicExchange()).with(ROUTING_KEY_B);
}
@Bean
public Binding bindingC() {
    return BindingBuilder.bind(queueC()).to(topicExchange()).with(ROUTING_KEY_C);
}


    /**
     * 声明死信交换机
     *
     * @return
     */
    @Bean
    public Exchange deadExchange() {
        return ExchangeBuilder.directExchange(DEAD_EXCHANGE).build();
    }

    /**
     * 声明死信队列
     *
     * @return
     */
    @Bean
    public Queue deadQueue() {
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }


    /**
     * 绑定死信的队列和交换机
     *
     * @param deadExchange
     * @param deadQueue
     * @return
     */
    @Bean
    public Binding deadBind(Exchange deadExchange, Queue deadQueue) {
        return BindingBuilder.bind(deadQueue).to(deadExchange).with(DEAD_ROUTING_KEY).noargs();
    }

    /**
     * 声明交换机，同channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT);
     *
     * @return
     */
    @Bean
    public Exchange bootExchange() {
        return ExchangeBuilder.directExchange(EXCHANGE).build();
    }

    /**
     * 声明队列，同channel.queueDeclare(QUEUE, true, false, false, null);
     * 绑定死信交换机及路由key
     *
     * @return
     */
    @Bean
    public Queue bootQueue() {
        Map<String, Object> args = new HashMap<>(3);
        //声明当前队列绑定的死信交换机
        args.put("x-dead-letter-exchange", DEAD_EXCHANGE);
        //声明当前队列的死信路由 key
        args.put("x-dead-letter-routing-key",DEAD_ROUTING_KEY);
        //声明队列的 TTL
        args.put("x-message-ttl", 1000);
        return QueueBuilder.durable(QUEUE).withArguments(args).build();
//        return QueueBuilder.durable(QUEUE)
//                .deadLetterExchange(DEAD_EXCHANGE)
//                .deadLetterRoutingKey(DEAD_ROUTING_KEY)
//                //声明队列属性有更改时需要删除队列
//                //给队列设置消息时长
//                .ttl(100000)
//                //队列最大长度
//                .maxLength(1)
//                .build();
    }

    /**
     * 绑定队列和交换机,同 channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY);
     *
     * @param bootExchange
     * @param bootQueue
     * @return
     */
    @Bean
    public Binding bootBind(Exchange bootExchange, Queue bootQueue) {
        return BindingBuilder.bind(bootQueue).to(bootExchange).with(ROUTING_KEY).noargs();
    }

}
